البرمجة

البنى في لغة جو

البنى (Structs) في لغة Go: دراسة معمقة ومفصلة

تُعتبر لغة Go (المعروفة أيضًا باسم Golang) واحدة من أبرز لغات البرمجة الحديثة التي تجمع بين البساطة والأداء العالي، وهي مناسبة لتطوير أنظمة وبرمجيات متعددة المجالات مثل تطبيقات الويب، الشبكات، والأنظمة الموزعة. ومن أهم المفاهيم التي توفرها لغة Go هي البنى (Structs)، التي تُعد حجر الأساس لبناء بيانات مركبة تسمح بتجميع عدة حقول مختلفة تحت اسم واحد.

في هذا المقال، سوف نتناول شرحًا تفصيليًا حول مفهوم البنى في لغة Go، طريقة تعريفها، استخدامها، كيفية التعامل معها، وكذلك مقارنة البنى بأنواع البيانات الأخرى. سنستعرض أيضًا كيفية استغلال البنى في البرمجة الكائنية (Object-Oriented Programming) داخل Go، بالرغم من أن اللغة لا تدعم الوراثة بشكل مباشر.


مفهوم البنى (Structs) في لغة Go

البنى في Go هي نوع بيانات مركب (composite data type) يُستخدم لتجميع مجموعة من المتغيرات التي قد تكون من أنواع مختلفة تحت اسم واحد. يمكن تشبيهها بـ “الكائنات” في لغات أخرى، لكنها تختلف في أنها لا تحتوي على وظائف أو طرق مضمّنة بالضرورة، بل هي مجرد حاوية للبيانات.

التعريف الأساسي للبنى

يتم تعريف بنية جديدة باستخدام الكلمة المفتاحية type متبوعة باسم البنية، ثم الكلمة المفتاحية struct، ثم مجموعة الحقول داخل أقواس معقوفة {}. على سبيل المثال:

go
type Person struct { Name string Age int Email string }

في المثال السابق، قمنا بتعريف بنية باسم Person تحتوي على ثلاثة حقول: Name من النوع string، و Age من النوع int، و Email من النوع string.


إنشاء متغير من نوع بنية

بعد تعريف البنية، يمكن إنشاء متغير من هذا النوع بطريقتين رئيسيتين:

  1. إنشاء متغير فارغ وملئ الحقول لاحقًا:

go
var p Person p.Name = "Ahmed" p.Age = 30 p.Email = "[email protected]"
  1. إنشاء متغير مع تهيئة القيم أثناء التصريح:

go
p := Person{ Name: "Sara", Age: 25, Email: "[email protected]", }

تهيئة القيم بدون تسمية الحقول

يمكن أيضًا تهيئة القيم دون تسمية الحقول، لكن ذلك يتطلب ترتيب القيم حسب ترتيب الحقول في تعريف البنية:

go
p := Person{"Ali", 22, "[email protected]"}

هذه الطريقة أقل وضوحًا ويُنصح باستخدام التهيئة مع تسمية الحقول للحفاظ على الوضوح.


الوصول إلى الحقول والتعديل عليها

يمكن الوصول إلى الحقول باستخدام النقطة .، سواء لقراءة القيم أو تعديلها:

go
fmt.Println(p.Name) // طباعة الاسم p.Age = 26 // تعديل العمر

استخدام المؤشرات مع البنى

في Go، يمكن استخدام المؤشرات للإشارة إلى بنية معينة لتجنب نسخ البيانات وتحسين الأداء. تعريف مؤشر لبنية يكون كالتالي:

go
var p *Person = &Person{Name: "Yousef", Age: 40}

عند استخدام مؤشر، يمكن الوصول إلى الحقول مباشرة باستخدام النقطة، دون الحاجة لفك الإشارة يدويًا:

go
fmt.Println(p.Name) // طباعة "Yousef" p.Age = 41 // تعديل العمر

السبب في ذلك هو أن Go تتولى فك الإشارة تلقائيًا عند الوصول إلى الحقول.


البنى والقيم الافتراضية

عند تعريف متغير بنوع بنية بدون تهيئة، يتم تعيين القيم الافتراضية لكل حقل وفقًا لنوعه، مثلاً:

  • الأعداد (int, float64, …) تكون قيمتها الافتراضية صفر.

  • النصوص (string) تكون فارغة.

  • المؤشرات تكون nil.

مثال:

go
var p Person fmt.Println(p.Age) // طباعة 0 fmt.Println(p.Name) // طباعة ""

البنى كقيم وقابليتها للتمرير

في Go، عندما يتم تمرير بنية إلى دالة، يتم تمرير نسخة من هذه البنية (pass by value)، مما يعني أن التعديلات على النسخة داخل الدالة لا تؤثر على النسخة الأصلية. لتعديل البنية الأصلية، يجب تمرير مؤشر إليها.

مثال يوضح ذلك:

go
func updateAge(p Person) { p.Age = 50 } func updateAgePointer(p *Person) { p.Age = 50 } func main() { person := Person{Name: "Nour", Age: 30} updateAge(person) fmt.Println(person.Age) // يطبع 30، لأن النسخة داخل الدالة تم تعديلها فقط updateAgePointer(&person) fmt.Println(person.Age) // يطبع 50، لأن المؤشر عدّل القيمة الأصلية }

تداخل البنى (Nested Structs)

يمكن تعريف بنية تحتوي على حقول من نوع بنى أخرى، مما يسمح بإنشاء بيانات معقدة ومتعددة المستويات.

مثال:

go
type Address struct { Street string City string Zip string } type Employee struct { Name string Age int Address Address }

يمكن بعد ذلك الوصول إلى الحقول المتداخلة باستخدام النقطة:

go
emp := Employee{ Name: "Huda", Age: 28, Address: Address{ Street: "123 Main St", City: "Cairo", Zip: "12345", }, } fmt.Println(emp.Address.City) // طباعة "Cairo"

الحقول المضمنة (Embedded Fields)

تدعم Go مفهوم الحقول المضمنة التي تسمح بتضمين بنية داخل بنية أخرى بدون اسم حقل محدد، وهذا يشبه الوراثة بشكل غير مباشر.

مثال:

go
type Person struct { Name string Age int } type Employee struct { Person // حقل مضمن من نوع Person Position string }

يمكن الوصول إلى حقول Person مباشرة عبر متغير Employee:

go
emp := Employee{ Person: Person{Name: "Omar", Age: 35}, Position: "Manager", } fmt.Println(emp.Name) // طباعة "Omar" fmt.Println(emp.Age) // طباعة 35 fmt.Println(emp.Position) // طباعة "Manager"

هذه الطريقة تتيح إعادة استخدام الكود وتوفير طريقة بسيطة لتوسيع البيانات.


البنى والطرق (Methods)

على الرغم من أن البنى هي مجرد مجموعات من الحقول، يمكن ربط طرق (Methods) بها لتعزيز وظائفها، وهذا يتيح نمط البرمجة الكائنية في Go.

مثال لتعريف طريقة على بنية:

go
func (p Person) Greet() string { return "Hello, my name is " + p.Name }

يمكن استدعاء الطريقة على أي متغير من نوع Person:

go
p := Person{Name: "Layla"} fmt.Println(p.Greet()) // طباعة: Hello, my name is Layla

طرق بمؤشر المستلم

يمكن تعريف طرق تستقبل مؤشر للبنية لتعديل القيم داخل البنية:

go
func (p *Person) HaveBirthday() { p.Age++ }

باستخدام هذه الطريقة يمكن تعديل محتوى البنية:

go
p := Person{Name: "Samir", Age: 40} p.HaveBirthday() fmt.Println(p.Age) // يطبع 41

مقارنة بين البنى (Structs) والخرائط (Maps)

في Go، يمكن استخدام البنى أو الخرائط لتخزين البيانات، لكن لكل منهما خصائص واستخدامات مختلفة.

الخاصية البنى (Structs) الخرائط (Maps)
الهيكل الثابت تحتوي على حقول ثابتة معروفة مسبقًا مفتوحة ديناميكيًا وتسمح بإضافة مفاتيح جديدة
الأداء أسرع بسبب الهيكل الثابت أبطأ قليلاً بسبب البحث الديناميكي
الأمان أكثر أمانًا بسبب التحقق من النوع في وقت الترجمة أقل أمانًا وقد تواجه أخطاء في وقت التشغيل
القابلية للتمرير تمرير نسخة أو مؤشر تمرير مؤشر إلى الخريطة
الاستخدام النموذجي تمثيل بيانات معروفة البناء تخزين بيانات غير منظمة أو متغيرة

مزايا البنى في لغة Go

  1. تنظيم البيانات: تسهل البنى تنظيم وتجميع البيانات ذات الصلة في هيكل واحد مما يسهل التعامل معها.

  2. الوضوح: تعطي تسمية الحقول وضوحًا في فهم طبيعة البيانات.

  3. التحكم في الذاكرة: باستخدام المؤشرات، يمكن تحسين أداء التطبيقات وتقليل نسخ البيانات.

  4. المرونة: إمكانية تضمين بنى أخرى وتكوين بنى متداخلة ومعقدة.

  5. التوافق مع البرمجة الكائنية: دعم ربط الطرق (methods) مع البنى.

  6. الأمان النوعي: توفر فحص أنواع قوي خلال الترجمة يمنع الأخطاء الشائعة.


أفضل الممارسات في استخدام البنى

  • استخدام التهيئة المسماة: للحفاظ على وضوح الكود وتجنب الأخطاء.

  • تمرير المؤشرات للعمليات الثقيلة: لتجنب نسخ البيانات غير الضروري.

  • تجنب الحقول العامة بدون داعٍ: من الأفضل التحكم في الحقول باستخدام الحقول الخاصة (بالبداية بحرف صغير) وتوفير واجهات تحكم إذا لزم الأمر.

  • استخدام الحقول المضمنة بحكمة: لتجنب تعقيد الوراثة غير المباشرة التي قد تؤدي إلى تعارضات في الحقول.

  • تعريف طرق مناسبة: لتحسين قابلية إعادة الاستخدام والوضوح.


ملخص وخصائص تقنية للبنى في Go

الخاصية الوصف
التعريف type Name struct { ... }
أنواع الحقول أي نوع بيانات، بما في ذلك بنى أخرى
تهيئة القيم تسمية الحقول أو بدون تسمية
تمريرها للوظائف بشكل نسخة أو مؤشر
دعم التداخل دعم بنى متداخلة وحقول مضمنة
دعم الطرق يمكن ربط طرق على البنى
الاستخدام الشائع تمثيل بيانات معقدة ومركبة

الخلاصة

البنى (Structs) تمثل ركيزة أساسية في لغة Go لبناء بيانات مركبة ومرنة، تمكّن المطورين من تنظيم المعلومات بشكل واضح ومنطقي. تكامل البنى مع المؤشرات، الحقول المضمنة، والطرق يجعلها أداة قوية تدعم تصميم برمجي منظم وفعال. باستخدام البنى، يمكن بناء هياكل بيانات متقدمة تلبي متطلبات التطبيقات الحديثة، مع الاستفادة من الأداء العالي والفعالية التي تتميز بها لغة Go.


المصادر والمراجع